/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.service.evaluation;

import cn.ac.nercis.pes.common.constant.evaluation.EvaluationModule;
import cn.ac.nercis.pes.common.constant.knowledge.TargetDeepness;
import cn.ac.nercis.pes.common.constant.project.FinishState;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.common.ResultCode;
import cn.ac.nercis.pes.model.dto.evaluation.planning.EvaluationRequireDTO;
import cn.ac.nercis.pes.model.dto.target.LayerTargetProcessDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationRequireRepository;
import cn.ac.nercis.pes.repository.dal.knowledge.KmEvaluationRequireRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.mapper.evaluation.EvaluationRequireMapper;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluationRequireDO;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.repository.model.project.SystemTargetDO;
import cn.ac.nercis.pes.service.project.SystemProcessService;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 测评配合需求信息服务类
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public record EvaluationRequireService(EvaluationRequireRepository evaluationRequireRepository,
                                       KmEvaluationRequireRepository kmEvaluationRequireRepository,
                                       SystemTargetRepository systemTargetRepository,
                                       SystemProcessService systemProcessService) {

    @Autowired
    public EvaluationRequireService {
    }

    public List<LayerTargetProcessDTO> findEvaluationProcess(@NonNull String systemId) {
        var evaluationRequires = evaluationRequireRepository.findAllBySystemId(systemId);
        Set<String> layerTargetIds = evaluationRequires.stream()
                .map(EvaluationRequireDO::getLayerTargetId)
                .collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(layerTargetIds)) {
            return Collections.emptyList();
        }
        return systemTargetRepository.findAllBySystemIdAndKmTargetIdIn(systemId, layerTargetIds)
                .stream()
                .map(kt -> {
                    long count = evaluationRequires.stream().filter(er -> StringUtils.equals(er.getLayerTargetId(), kt.getKmTargetId())).count();
                    long finishCount = evaluationRequires.stream()
                            .filter(er -> StringUtils.equals(er.getLayerTargetId(), kt.getKmTargetId()) && er.getFinishState() == FinishState.FINISH)
                            .count();
                    return LayerTargetProcessDTO.builder()
                            .systemId(systemId)
                            .targetId(kt.getKmTargetId())
                            .name(kt.getKmName())
                            .state(count == finishCount ? FinishState.FINISH : FinishState.INCOMPLETE)
                            .build();
                })
                .collect(Collectors.toList());
    }

    public Result<String> setEvaluationRequireFinishState(@NonNull String systemId, @NonNull String layerTargetId, @NonNull String layerTargetName, FinishState state) {
        List<EvaluationRequireDO> evaluationRequires = this.evaluationRequireRepository.findAllBySystemIdAndLayerTargetId(systemId, layerTargetId);
        if (CollectionUtils.isNotEmpty(evaluationRequires)) {
            evaluationRequires.forEach(er -> {
                er.setFinishState(state);
                er.setModifyTime(LocalDateTime.now());
            });
            this.evaluationRequireRepository.saveAll(evaluationRequires);
        }
        this.systemProcessService.setSystemProcess(systemId, EvaluationModule.fromNameByEvaRequire(layerTargetName), state);
        return Result.success(layerTargetId);
    }

    public List<EvaluationRequireDTO> findEvaluationRequires(@NonNull String systemId, @NonNull String layerTargetId) {
        return evaluationRequireRepository.findAllBySystemIdAndLayerTargetId(systemId, layerTargetId)
                .stream()
                .map(EvaluationRequireMapper.INSTANCE::toDTO)
                .collect(Collectors.toList());
    }

    public Result<EvaluationRequireDTO> saveEvaluationRequires(EvaluationRequireDTO evaluationRequire) {
        try {
            if (StringUtils.isEmpty(evaluationRequire.getId())) {
                return create(evaluationRequire);
            } else {
                return modify(evaluationRequire);
            }
        } catch (Exception e) {
            log.error("保存测评配合需求信息出错：", e);
            return Result.failed();
        }
    }

    public Result<String> batchSaveEvaluationRequires(List<EvaluationRequireDTO> evaluationRequire) {
        Result<String> result = Result.success();
        for (EvaluationRequireDTO item : evaluationRequire) {
            Result<EvaluationRequireDTO> itemResult = saveEvaluationRequires(item);
            if (!itemResult.isSuccess()) {
                result = Result.failed(ResultCode.FAIL);
                break;
            }
        }
        return result;
    }

    public Result<String> deleteEvaluationRequires(String id) {
        try {
            return evaluationRequireRepository.findById(id)
                    .map(entity -> {
                        evaluationRequireRepository.delete(entity);
                        return entity;
                    })
                    .map(EvaluationRequireDO::getId)
                    .map(Result::success)
                    .orElse(Result.failed());
        } catch (Exception ex) {
            log.error("删除测评配合需求信息出错：", ex);
            return Result.failed();
        }
    }

    public Result<String> batchDeleteEvaluationRequires(Set<String> ids) {
        Result<String> result = Result.success();
        for (String id : ids) {
            Result<String> itemResult = deleteEvaluationRequires(id);
            if (!itemResult.isSuccess()) {
                result = itemResult;
                break;
            }
        }
        return result;
    }

    public void initEvaluationRequire(SystemDO systemDO) {
        this.systemTargetRepository.findAllBySystemId(systemDO.getId())
                .stream()
                .filter(st -> st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .forEach(this::initEvaluationRequire);
    }

    private void initEvaluationRequire(SystemTargetDO layerTarget) {
        var requires = kmEvaluationRequireRepository.findAllByLayerTarget(layerTarget.getKmName())
                .stream()
                .map(er -> EvaluationRequireDO.builder()
                        .systemId(layerTarget.getSystemId())
                        .layerTargetId(layerTarget.getKmTargetId())
                        .project(er.getProject())
                        .require(er.getRequire())
                        .finishState(FinishState.NONE)
                        .build())
                .peek(er -> er.setCreateTime(LocalDateTime.now()))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(requires)) {
            evaluationRequireRepository.saveAll(requires);
        }
    }

    private Result<EvaluationRequireDTO> create(EvaluationRequireDTO evaluationRequire) {
        return Optional.of(EvaluationRequireMapper.INSTANCE.toDO(evaluationRequire))
                .map(e -> {
                    e.setFinishState(FinishState.INCOMPLETE);
                    e.setCreateTime(LocalDateTime.now());
                    return e;
                })
                .map(evaluationRequireRepository::save)
                .map(EvaluationRequireMapper.INSTANCE::toDTO)
                .map(Result::success)
                .orElse(Result.failed());
    }

    private Result<EvaluationRequireDTO> modify(EvaluationRequireDTO evaluationRequire) {
        EvaluationRequireDO source = this.evaluationRequireRepository.findById(evaluationRequire.getId()).orElse(null);
        if (Objects.isNull(source)) {
            return Result.failed(ResultCode.NOT_FOUND);
        }
        return Optional.of(source)
                .map(s -> {
                    s.setProject(evaluationRequire.getProject());
                    s.setRequire(evaluationRequire.getRequire());
                    s.setFinishState(FinishState.INCOMPLETE);
                    s.setModifyTime(LocalDateTime.now());
                    return s;
                })
                .map(evaluationRequireRepository::save)
                .map(EvaluationRequireMapper.INSTANCE::toDTO)
                .map(Result::success)
                .orElse(Result.failed(ResultCode.FAIL));
    }

}